package core

import (
	options "PcapCapture/src/options"
	"fmt"
	"github.com/google/gopacket/pcap"
	"github.com/ipipdotnet/ipdb-go"
	gonet "github.com/shirou/gopsutil/v3/net"
	"github.com/shirou/gopsutil/v3/process"
	"strconv"
	"sync"
	"time"
)

var (
	infoChannel chan PacketInfo // 用于在 parser 和 saver 之间传递 PacketInfo，作为消息队列使用
	endChannel  chan int        // 用于传递全局的程序停止信号
	ipProcess   sync.Map        // 用于将ip 信息和 进程对应起来， key : "ip:端口" value: gopsutil/process
)

func init() {
	infoChannel = make(chan PacketInfo, 10000)
	endChannel = make(chan int)
	go refreshIpProcess()
}

func Run(fillter string, waitTime int, timeOut int, IpOpt options.IpInfoLogOpt) {

	// 正式运行模式
	var wg sync.WaitGroup
	go findDevice(fillter, timeOut, infoChannel)
	wg.Add(1)
	go NewInfoSaver(infoChannel, &wg, IpOpt)
	time.Sleep(time.Duration(waitTime) * time.Minute)
	endChannel <- 1
	wg.Wait()
	return

}

func findDevice(fillter string, timeOut int, message chan PacketInfo) {

	prevDevices := make(map[string]bool)
	db, _ := ipdb.NewCity("./data/qqwry.ipdb")

	for {
		devices, _ := pcap.FindAllDevs()
		currentDevices := make(map[string]bool)

		// 检查新增接口
		var device string
		for _, dev := range devices {
			currentDevices[dev.Name] = true
			if !prevDevices[dev.Name] {
				if len(dev.Addresses) > 0 {
					device = dev.Name

					go doParser(dev.Name, device, fillter, timeOut, message, db)
					fmt.Println("新增设备:", device)
				}

			}
		}

		// 检查移除的接口
		for name := range prevDevices {
			if !currentDevices[name] {
				fmt.Println("移除设备:", name)
			}
		}
		prevDevices = currentDevices

		time.Sleep(500 * time.Millisecond)
	}
}

func doParser(dev string, devName string, fillter string, timeOut int, message chan PacketInfo, db *ipdb.City) {
	// 获得CaptureRequest
	handleManager, err := NewSingleHandleManager(dev, WithFilter(fillter), WithTimeout(time.Millisecond*time.Duration(timeOut)))

	//defer 保证协程退出的时候，handle 也自动关闭
	defer handleManager.Close()

	if err != nil {
		fmt.Println(devName + "初始化handle失败")
		return
	}

	// 执行Parser
	parser := NewParser(devName, handleManager.handle, message, db)

	//测试执行情况
	fmt.Printf("%s开始监听\n", devName)
	parser.ParsePacketResource()

}

// 通过设置的时间间隔，刷新IpProcess 表
func refreshIpProcess() {
	// 获取所有 TCP 连接
	ticker := time.NewTicker(time.Second)
	for {
		select {
		case <-ticker.C:
			conns, err := gonet.Connections("tcp")
			if err != nil {
				fmt.Println("获得TCP 连接失败:", err)
			}

			for _, conn := range conns {
				singleProcess, ok := process.NewProcess(conn.Pid)
				if ok != nil {
					continue
				}
				ipProcess.Store(string(conn.Laddr.IP)+":"+strconv.Itoa(int(conn.Laddr.Port)), singleProcess)
			}
		case <-endChannel:
			ticker.Stop()
			return
		}
	}

}
